# -*- coding: utf-8 -*-
import urllib3

urllib3.disable_warnings()

import requests
import openpyxl
from openpyxl import load_workbook,utils
from concurrent.futures import ThreadPoolExecutor

from commons.logger import log
from utils.encrypt_tools import DataEncrypt


class Network:
    def get_nasname_by_ip(self, ip):
        """
        根据 IP 获取 nasname
        :param ip:
        :return:
        """
        path = f"https://{ip}:9443/ugreen/v1/wizard/is_initialize?"
        log.info(f"开始尝试从 IP {ip} 获取 nasname，请求路径为 {path}")

        try:
            log.debug(f"正在向 {path} 发送 GET 请求，超时时间设置为 0.1 秒，忽略 SSL 验证")
            response = requests.get(path, timeout=0.2, verify=False)
            log.debug(f"收到来自 {path} 的响应，状态码为 {response.status_code}")

            if response.status_code == 200:
                log.info(f"IP {ip} 的响应状态码为 200，开始解析 JSON 数据")
                nasname = response.json().get('data', {}).get('device_name')
                if nasname:
                    log.info(f"成功从 IP {ip} 获取到 nasname: {nasname}")
                else:
                    log.warning(f"IP {ip} 的响应状态码为 200，但未找到有效的 nasname")
                return nasname
            else:
                log.warning(f"IP {ip} 的响应状态码不为 200，实际状态码为 {response.status_code}，无法获取 nasname")
                return None
        except requests.exceptions.Timeout:
            log.warning(f"请求 IP {ip} 超时，超时时间为 0.1 秒，无法获取 nasname")
            return None
        except requests.exceptions.RequestException as e:
            log.error(f"请求 IP {ip} 时发生网络错误，异常信息: {e}，无法获取 nasname")
            return None
        except ValueError as e:
            log.error(f"解析 IP {ip} 的响应 JSON 数据时发生错误，异常信息: {e}，无法获取 nasname")
            return None
        except Exception as e:
            log.critical(f"发生未知错误，IP: {ip}，异常信息: {e}，无法获取 nasname")
            return None

    def get_ip_by_prefix(self, network_prefix, nasname, thread_num=10):
        """
        根据网段前缀列表和 nasname 获取匹配的 IP 地址，使用多线程
        :param network_prefix: 网络前缀列表，如 ['172.17.20', '172.17.30']
        :param nasname: 设备名称
        :param thread_num: 线程数
        :return: 匹配的 IP 地址，如果未找到则返回 None
        """
        if isinstance(network_prefix, str):
            network_prefix = [network_prefix]

        log.info(f"开始根据网段前缀列表 {network_prefix} 和 nasname {nasname} 查找匹配的 IP 地址，使用 {thread_num} 个线程")

        all_ips = []
        for prefix in network_prefix:
            ips = [f"{prefix}.{i}" for i in range(1, 256)]
            all_ips.extend(ips)

        total_ips = len(all_ips)
        log.info(f"生成了 {total_ips} 个可能的 IP 地址进行检查")

        def check_ip(ip):
            name = self.get_nasname_by_ip(ip)
            if name and name == nasname:
                return ip
            return None

        with ThreadPoolExecutor(max_workers=thread_num) as executor:
            results = executor.map(check_ip, all_ips)

        for result in results:
            if result:
                log.info(f"找到匹配的 IP 地址: {result}，对应的 nasname 为 {nasname}")
                return result

        log.info(f"遍历完所有 {total_ips} 个 IP 地址后，未找到与 nasname {nasname} 匹配的 IP 地址")
        return None

    def verify_check(self,host,username):
        """获取公钥接口"""
        path = f'http://{host}:9999/ugreen/v1/verify/check'
        data = {
            "username": username
        }
        try:
            result = requests.post(path, json=data)
            return result.headers.get('X-Rsa-Token')
        except requests.ConnectionError as e:
            log.error(f"网络连接出现问题，获取公钥请求失败: {e}")
            raise  # 可以选择抛出，或者根据需要返回合适的值并记录日志，这里选择抛出以便上层处理
        except requests.Timeout as e:
            log.error(f"获取公钥请求超时: {e}")
            raise
        except Exception as e:
            log.error(f"获取公钥接口出现其他异常: {e}")
            raise

    def login(self,host,username, password):
        """
        重新封装一个登录方法，根据excel表格中的信息登录
        :return:
        """
        path = f'http://{host}:9999/ugreen/v1/verify/login'
        Rsa_Token = self.verify_check(host,username)
        if Rsa_Token is None:
            log.error("获取公钥失败，无法进行登录操作")
            return None
        try:
            encrypted_passwd = DataEncrypt().rsa_passwd(Rsa_Token, password)
            data_login = {
                "username": username,
                "password": encrypted_passwd,
            }
            response = requests.post(path, json=data_login)
            if response.status_code == 200:
                try:
                    token = response.json().get('data').get('token')
                    log.info(f"登录成功，获取到的token: {token}")
                    return token
                except ValueError as json_error:
                    log.error(f"响应数据无法解析为JSON格式，无法获取token: {json_error}")
                    return None
            else:
                log.error(f"登录请求失败，状态码: {response.status_code}")
                return None
        except requests.ConnectionError as e:
            log.error(f"网络连接出现问题，登录过程出现异常: {e}")
            raise
        except requests.Timeout as e:
            log.error(f"登录请求超时: {e}")
            raise
        except Exception as e:
            log.error(f"登录过程出现其他异常: {e}")
            raise

    def get_nas_name(self,host,token):
        """获取设备名称"""
        path = f'http://{host}:9999/ugreen/v1/sysinfo/machine/common'
        params = {'token': token}
        try:
            response = requests.get(path,params=params)
            if response.status_code == 200:
                try:
                    nasname = response.json().get('data').get('common').get('nas_name')
                    log.info(f"登录成功，获取到的nasname: {nasname}")
                    return nasname
                except ValueError as json_error:
                    log.error(f"响应数据无法解析为JSON格式，无法获取token: {json_error}")
                    return None
            else:
                log.error(f"获取设备名称请求失败，状态码: {response.status_code}")
                return None
        except requests.ConnectionError as e:
            log.error(f"网络连接出现问题，获取设备名称请求失败: {e}")
            raise  # 可以选择抛出，或者根据需要返回合适的值并记录日志，这里选择抛出以便上层处理
        except requests.Timeout as e:
            log.error(f"获取设备名称请求超时: {e}")
            raise
        except Exception as e:
            log.error(f"获取设备名称出现其他异常: {e}")
            raise

    def write_nasname_to_excel(self, file_path):
        """
        向Excel表格中写入设备名称。

        :param file_path: Excel文件路径
        """
        try:
            # 加载工作簿
            log.info(f"开始加载Excel文件: {file_path}")
            workbook = load_workbook(file_path)
            # 获取活动工作表
            sheet = workbook.active

            # 定义列索引
            nas_name_index = 3  # 假设SN设备名称在第3列
            host_index = 6  # 假设设备IP在第6列
            username_index = 7  # 假设账号第7列
            password_index = 8  # 假设密码第8列

            # 遍历行，跳过标题行
            log.info("开始遍历Excel表格中的行")
            for row_num, row in enumerate(sheet.iter_rows(min_row=2, values_only=False), start=2):
                host = row[host_index - 1].value
                username = row[username_index - 1].value
                password = row[password_index - 1].value

                if not host or not username or not password:
                    log.warning(f"第 {row_num} 行的主机、用户名或密码为空，跳过该行")
                    continue

                try:
                    log.info(f"开始处理第 {row_num} 行，主机: {host}，用户名: {username}")
                    token = self.login(host, username, password)
                    if token:
                        nas_name = self.get_nas_name(host, token)
                        row[nas_name_index - 1].value = nas_name
                        log.info(f"第 {row_num} 行的设备名称已更新为: {nas_name}")
                    else:
                        log.error(f"第 {row_num} 行登录失败，无法获取设备名称")
                        row[nas_name_index - 1].value = "登录失败"
                except Exception as e:
                    log.error(f"处理第 {row_num} 行时出现异常: {e}")
                    row[nas_name_index - 1].value = f"异常: {e}"

            # 保存工作簿
            log.info("遍历完成，开始保存Excel文件")
            workbook.save(file_path)
            log.info(f"Excel文件 {file_path} 保存成功")

        except FileNotFoundError:
            log.error(f'路径错误或文件不存在：{file_path}')
        except PermissionError:
            log.error(f'没有权限访问文件：{file_path}')
        except Exception as e:
            log.error(f'处理Excel文件时出现未知异常：{e}')

if __name__ == "__main__":

    network = Network()
    # network_prefix = ["172.17.20","172.17.21"]
    # nasname = "DXP8800-73AB"
    # thread_num = 20  # 指定线程数
    # ip = network.get_ip_by_prefix(network_prefix, nasname)
    # if ip:
    #     print(f"找到匹配的 IP 地址: {ip}")
    # else:
    #     print("未找到匹配的 IP 地址")
    # # host = "172.17.20.57"
    # # username = "test3"
    # # password = "Aa123456@"
    # # token = network.login(host,username,password)
    # # print(token)
    # # nasname = network.get_nas_name(host,token)
    # # print(nasname)
    network.write_nasname_to_excel('设备账号信息.xlsx')